前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# 网络UDP dgram

30 秒速记

  • UDP = 寄明信片:不建连接、不保证送到、不保证顺序,可能重复
  • 每次 send 就是一个独立数据报,边界保留,不会像 TCP 那样粘包
  • 包别太大:IPv4 理论上限 65507 字节,实际尽量控制在 1472 字节以内,避免 IP 分片
  • 要可靠就自己加序号、确认、重传、去重,或者直接用 QUIC 这类成熟协议
  • 服务端要校验来源和包长,回包不要比请求大太多,防止被拿去做放大攻击

UDP 可以理解成寄明信片,写好地址扔进邮筒就完事,不用先打电话确认对方在不在,代价是可能寄丢、可能乱序。 好处是快、开销小、每次发的就是一个完整的包,不用像 TCP 那样自己切消息边界,所以实时音视频、游戏状态同步、DNS 查询、日志上报都爱用它。要注意包大小,以太网 MTU 是 1500 字节,扣掉 IP 和 UDP 头大约剩 1472 字节,超过就会分片,任何一片丢了整个包都没了。业务真要可靠送达,要么自己补确认重传,要么直接换 TCP 或 QUIC。

理解「网络UDP dgram」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 network 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

UDP 和 TCP 的差别放在一起看更好记:

TCP(net) UDP(dgram)
连接 先三次握手 无连接,直接发
可靠性 丢了自动重传 丢了就丢了
顺序 保证有序 可能乱序
边界 字节流,会粘包拆包 一次 send 就是一个包
典型场景 HTTP、数据库连接 DNS、音视频、游戏、日志上报

包大小是 UDP 里最实际的坑。IPv4 下一个 UDP 包的负载理论上最多 65507 字节(65535 减去 20 字节 IP 头和 8 字节 UDP 头),但以太网 MTU 一般是 1500,超过 1472 字节就要分片。分片在公网上丢失率高,很多协议因此把单包控制在 1200 字节左右,QUIC 默认也是这个量级。

在 Node 里,超过上限的包 send 会直接报 EMSGSIZE;没超上限但需要分片的包,发送端不会有任何提示,只会在接收端「神秘消失」,排查时要想到这一层。

面试官追问

追问 1send 回调没报错,接收端却少了几条消息,是 dgram 模块的问题吗?
参考回答

不是。send 回调成功只说明数据交给了操作系统,UDP 本身不保证送达。要确认就在应用层给消息加序号,接收方回 ack,发送方超时重发。

追问 2单个包塞了 8KB 的 JSON,内网好好的,跨机房就经常丢,为什么?
参考回答

8KB 远超 MTU,会被拆成好几个 IP 分片,跨网络时任一分片丢了,整包就作废,有的防火墙还直接丢分片。把单包控制在 1200 到 1400 字节左右,大数据要么切片编号,要么换 TCP。

追问 3游戏位置同步用 UDP,聊天消息要不要也走 UDP?
参考回答

位置这种「新的覆盖旧的」数据丢一两个没关系,适合 UDP;聊天必须送达且有序,走 TCP 或 WebSocket 省心得多。硬要统一 UDP,就得自己写一套确认、重传和排序。

追问 4收到的 UDP 包为什么不能直接按 rinfo.address 信任来源?
参考回答

UDP 没有握手,源地址可以伪造。拿它做鉴权不靠谱,敏感操作要在包里带签名或令牌;回包也别比请求大太多,否则会被伪造源地址拿去做反射放大攻击。

# 模块概览

30 秒速记

  • dgram = Node 对 UDP socket 的封装,比 net 简单:没有连接、没有流
  • 创建:dgram.createSocket('udp4' | 'udp6'),收消息要先 bind(port)
  • 核心事件:listening、message(msg, rinfo)、error、close
  • msg 是 Buffer,rinfo 里有 address、family、port、size
  • 用完要 close(),不然 socket 一直挂在事件循环上,进程不退出

dgram 就是 Node 里用来收发 UDP 包的模块,API 很少,记住「创建、绑定、收、发、关」五步就够。 dgram.createSocket('udp4') 建一个 socket,要收数据就 bind 到一个端口,然后监听 message,回调里第一个参数是 Buffer,第二个 rinfo 告诉你是谁发来的;发数据用 socket.send(buf, port, host, cb)。它比 net 简单是因为不用管连接状态和流的背压,但可靠性、顺序这些活就落到业务自己头上了。另外 error 一定要监听,端口被占用这种错误不监听会直接把进程搞崩。

dgram模块是对UDP socket的一层封装,相对net模块简单很多,下面看例子。

理解「模块概览」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

面试官追问

追问 1dgram 比 net 简单,所以 UDP 服务一定更好维护?
参考回答

不一定。API 简单是因为它什么都不保证,一旦业务要确认、重传、去重、排序,这些逻辑全得自己写,最后往往比直接用 TCP 复杂。

追问 2服务端 bind(33333) 启动就崩了,报 EADDRINUSE,怎么处理?
参考回答

端口被占了,而且没监听 error 所以直接抛异常。加上 socket.on('error', ...) 记日志并退出;确实需要多进程共用端口时,创建时传 { type: 'udp4', reuseAddr: true }。

追问 3UDP 小工具发完一个包,进程一直不退出,为什么?
参考回答

socket 还开着,事件循环认为还有活要干。发完在 send 回调里 socket.close();或者调用 socket.unref(),让它不阻止进程退出。

追问 4message 回调里直接 JSON.parse(msg),偶尔整个服务挂掉,怎么改?
参考回答

UDP 谁都能往端口扔包,收到半截或乱码很正常。先看 rinfo.size 是否在合理范围,再用 try/catch 包住解析,非法包直接丢弃并计数。

追问 5只跟一个固定的对端通信,每次 send 都要写地址吗?
参考回答

Node 12 起可以先 socket.connect(port, host),之后 send(buf) 不用再带地址,而且只会收到这个对端发来的包。这里的 connect 只是在本地记住对端,不会真的握手。

# UDP客户端 vs UDP服务端

30 秒速记

  • UDP 里的「客户端/服务端」只是角色叫法:一个 bind 等着收,一个直接 send
  • 两边都用 dgram.createSocket('udp4') 创建,没有握手和连接
  • 服务端:listening 确认绑定成功 → message(msg, remote) 拿到内容和来源
  • 客户端:send(buf, port, host, cb),回调成功 ≠ 对方收到
  • 一次性客户端在回调里 close();连续发送的要复用同一个 socket,别每条关一次

UDP 没有连接,所谓服务端就是先 bind 到一个固定端口等包的那一方,客户端就是知道这个地址、直接往里扔包的那一方。 服务端监听 listening 确认绑好了,再在 message 里拿到 msg 和 remote,remote.address 和 remote.port 就是发送方,想回包直接 server.send(reply, remote.port, remote.address)。客户端不用 bind,第一次 send 时系统会自动分一个随机端口,日志里那个 58940 就是这么来的。要注意 send 的回调只说明包已经交给系统了,对方收没收到它不知道。

首先,启动UDP server,监听来自端口33333的请求。

server.js

// 例子:UDP服务端
var PORT = 33333;
var HOST = '127.0.0.1';

var dgram = require('dgram');
var server = dgram.createSocket('udp4');

server.on('listening', function () {
    var address = server.address();
    console.log('UDP Server listening on ' + address.address + ":" + address.port);
});

server.on('message', function (message, remote) {
    console.log(remote.address + ':' + remote.port +' - ' + message);
});

server.bind(PORT, HOST);
@前端进阶之旅: 代码已经复制到剪贴板

然后,创建UDP socket,向端口33333发送请求。

client.js

// 例子:UDP客户端
var PORT = 33333;
var HOST = 
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
      • 模块概览
      • UDP客户端 vs UDP服务端
      • 广播
      • 相关链接
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

网络UDP dgram|Nodejs专题系列

# 网络UDP dgram

30 秒速记

  • UDP = 寄明信片:不建连接、不保证送到、不保证顺序,可能重复
  • 每次 send 就是一个独立数据报,边界保留,不会像 TCP 那样粘包
  • 包别太大:IPv4 理论上限 65507 字节,实际尽量控制在 1472 字节以内,避免 IP 分片
  • 要可靠就自己加序号、确认、重传、去重,或者直接用 QUIC 这类成熟协议
  • 服务端要校验来源和包长,回包不要比请求大太多,防止被拿去做放大攻击

UDP 可以理解成寄明信片,写好地址扔进邮筒就完事,不用先打电话确认对方在不在,代价是可能寄丢、可能乱序。 好处是快、开销小、每次发的就是一个完整的包,不用像 TCP 那样自己切消息边界,所以实时音视频、游戏状态同步、DNS 查询、日志上报都爱用它。要注意包大小,以太网 MTU 是 1500 字节,扣掉 IP 和 UDP 头大约剩 1472 字节,超过就会分片,任何一片丢了整个包都没了。业务真要可靠送达,要么自己补确认重传,要么直接换 TCP 或 QUIC。

理解「网络UDP dgram」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 network 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

UDP 和 TCP 的差别放在一起看更好记:

TCP(net) UDP(dgram)
连接 先三次握手 无连接,直接发
可靠性 丢了自动重传 丢了就丢了
顺序 保证有序 可能乱序
边界 字节流,会粘包拆包 一次 send 就是一个包
典型场景 HTTP、数据库连接 DNS、音视频、游戏、日志上报

包大小是 UDP 里最实际的坑。IPv4 下一个 UDP 包的负载理论上最多 65507 字节(65535 减去 20 字节 IP 头和 8 字节 UDP 头),但以太网 MTU 一般是 1500,超过 1472 字节就要分片。分片在公网上丢失率高,很多协议因此把单包控制在 1200 字节左右,QUIC 默认也是这个量级。

在 Node 里,超过上限的包 send 会直接报 EMSGSIZE;没超上限但需要分片的包,发送端不会有任何提示,只会在接收端「神秘消失」,排查时要想到这一层。

面试官追问

追问 1send 回调没报错,接收端却少了几条消息,是 dgram 模块的问题吗?
参考回答

不是。send 回调成功只说明数据交给了操作系统,UDP 本身不保证送达。要确认就在应用层给消息加序号,接收方回 ack,发送方超时重发。

追问 2单个包塞了 8KB 的 JSON,内网好好的,跨机房就经常丢,为什么?
参考回答

8KB 远超 MTU,会被拆成好几个 IP 分片,跨网络时任一分片丢了,整包就作废,有的防火墙还直接丢分片。把单包控制在 1200 到 1400 字节左右,大数据要么切片编号,要么换 TCP。

追问 3游戏位置同步用 UDP,聊天消息要不要也走 UDP?
参考回答

位置这种「新的覆盖旧的」数据丢一两个没关系,适合 UDP;聊天必须送达且有序,走 TCP 或 WebSocket 省心得多。硬要统一 UDP,就得自己写一套确认、重传和排序。

追问 4收到的 UDP 包为什么不能直接按 rinfo.address 信任来源?
参考回答

UDP 没有握手,源地址可以伪造。拿它做鉴权不靠谱,敏感操作要在包里带签名或令牌;回包也别比请求大太多,否则会被伪造源地址拿去做反射放大攻击。

# 模块概览

30 秒速记

  • dgram = Node 对 UDP socket 的封装,比 net 简单:没有连接、没有流
  • 创建:dgram.createSocket('udp4' | 'udp6'),收消息要先 bind(port)
  • 核心事件:listening、message(msg, rinfo)、error、close
  • msg 是 Buffer,rinfo 里有 address、family、port、size
  • 用完要 close(),不然 socket 一直挂在事件循环上,进程不退出

dgram 就是 Node 里用来收发 UDP 包的模块,API 很少,记住「创建、绑定、收、发、关」五步就够。 dgram.createSocket('udp4') 建一个 socket,要收数据就 bind 到一个端口,然后监听 message,回调里第一个参数是 Buffer,第二个 rinfo 告诉你是谁发来的;发数据用 socket.send(buf, port, host, cb)。它比 net 简单是因为不用管连接状态和流的背压,但可靠性、顺序这些活就落到业务自己头上了。另外 error 一定要监听,端口被占用这种错误不监听会直接把进程搞崩。

dgram模块是对UDP socket的一层封装,相对net模块简单很多,下面看例子。

理解「模块概览」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

面试官追问

追问 1dgram 比 net 简单,所以 UDP 服务一定更好维护?
参考回答

不一定。API 简单是因为它什么都不保证,一旦业务要确认、重传、去重、排序,这些逻辑全得自己写,最后往往比直接用 TCP 复杂。

追问 2服务端 bind(33333) 启动就崩了,报 EADDRINUSE,怎么处理?
参考回答

端口被占了,而且没监听 error 所以直接抛异常。加上 socket.on('error', ...) 记日志并退出;确实需要多进程共用端口时,创建时传 { type: 'udp4', reuseAddr: true }。

追问 3UDP 小工具发完一个包,进程一直不退出,为什么?
参考回答

socket 还开着,事件循环认为还有活要干。发完在 send 回调里 socket.close();或者调用 socket.unref(),让它不阻止进程退出。

追问 4message 回调里直接 JSON.parse(msg),偶尔整个服务挂掉,怎么改?
参考回答

UDP 谁都能往端口扔包,收到半截或乱码很正常。先看 rinfo.size 是否在合理范围,再用 try/catch 包住解析,非法包直接丢弃并计数。

追问 5只跟一个固定的对端通信,每次 send 都要写地址吗?
参考回答

Node 12 起可以先 socket.connect(port, host),之后 send(buf) 不用再带地址,而且只会收到这个对端发来的包。这里的 connect 只是在本地记住对端,不会真的握手。

# UDP客户端 vs UDP服务端

30 秒速记

  • UDP 里的「客户端/服务端」只是角色叫法:一个 bind 等着收,一个直接 send
  • 两边都用 dgram.createSocket('udp4') 创建,没有握手和连接
  • 服务端:listening 确认绑定成功 → message(msg, remote) 拿到内容和来源
  • 客户端:send(buf, port, host, cb),回调成功 ≠ 对方收到
  • 一次性客户端在回调里 close();连续发送的要复用同一个 socket,别每条关一次

UDP 没有连接,所谓服务端就是先 bind 到一个固定端口等包的那一方,客户端就是知道这个地址、直接往里扔包的那一方。 服务端监听 listening 确认绑好了,再在 message 里拿到 msg 和 remote,remote.address 和 remote.port 就是发送方,想回包直接 server.send(reply, remote.port, remote.address)。客户端不用 bind,第一次 send 时系统会自动分一个随机端口,日志里那个 58940 就是这么来的。要注意 send 的回调只说明包已经交给系统了,对方收没收到它不知道。

首先,启动UDP server,监听来自端口33333的请求。

server.js

// 例子:UDP服务端
var PORT = 33333;
var HOST = '127.0.0.1';

var dgram = require('dgram');
var server = dgram.createSocket('udp4');

server.on('listening', function () {
    var address = server.address();
    console.log('UDP Server listening on ' + address.address + ":" + address.port);
});

server.on('message', function (message, remote) {
    console.log(remote.address + ':' + remote.port +' - ' + message);
});

server.bind(PORT, HOST);
@前端进阶之旅: 代码已经复制到剪贴板

然后,创建UDP socket,向端口33333发送请求。

client.js

// 例子:UDP客户端
var PORT = 33333;
var HOST = '127.0.0.1';

var dgram = require('dgram');
var message = Buffer.from('My KungFu is Good!');

var client = dgram.createSocket('udp4');

client.send(message, PORT, HOST, function(err, bytes) {
    if (err) throw err;
    console.log('UDP message sent to ' + HOST +':'+ PORT);
    client.close();
});
@前端进阶之旅: 代码已经复制到剪贴板

运行 server.js。

node server.js
@前端进阶之旅: 代码已经复制到剪贴板

运行 client.js。

➜  2016.12.22-dgram git:(master) ✗ node client.js 
UDP message sent to 127.0.0.1:33333
@前端进阶之旅: 代码已经复制到剪贴板

服务端打印日志如下

UDP Server listening on 127.0.0.1:33333
127.0.0.1:58940 - My KungFu is Good!
@前端进阶之旅: 代码已经复制到剪贴板

← 4.7 网络TCP net4.9 域名解析 dns →

fe